using UnityEngine;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using SimpleJSON;
using UnityEngine.Audio;
using Unity.Entities;
using Unity.Physics.Systems;
using Unity.Rendering;
using Gj;
using Gj.Galaxy.Logic;
using Gj.Galaxy.Network;
using MessagePack;
using UnityEngine.Android;
using WebSocketSharp;
using Exception = System.Exception;
using Random = System.Random;

namespace Gj
{
    public class CachePrefix
    {
        public const string API = "api";
        public const string REFERER = "referer";
        public const string RELATION = "relation";
    }

    public class CacheKey
    {
        public const string ALL = "ALL";
        public const string BGM = "BGM";
        public const string EFF = "EFF";
        public const string LANGUAGE = "language";
        public const string QUALITY = "quality";
        public const string FULL = "full";
        public const string SCALE = "scale";
        public const string RESOLUTION = "resolution";
        public const string SIZE = "size";
        public const string FRAME_RATE = "frameRate";
        public const string NEW_STEP = "newStep";
    }

    public enum UIEvent
    {
        None,
        PullUp,
        PullDown,
        End,
        Start,
        Open,
        Close,
        Show,
        Hide,
        Submit,
        Confirm,
        Cancel,
        Enter,
        Move,
        Leave,
        TimeOut,
        Win,
        Lose,
        Down,
        Up,
        Switch,
        Create,
        Del,
        Select,
        Click,
        DoubleClick,
        LongPress,
        Touch,
        Edit,
        Ok,
        Refresh,
        Receive,
        Buy,
        Sell,
        Exchange,
        Join,
        Back,
        Add,
        Reduce,
        Ad,
        Free,
        Change,
        Drag,
        Drop,
        Upgrade,
        Next,
        Prev,
        Exit,
        Upload,
        Like,
        Collect,
        Copy,
        Share,
        Follow,
        Ready,
        Out,
        Feedback,
        Sync,
        Remove,
    }

    public enum SoundType
    {
        None,
        Click,
        Back,
        Switch,
        Select,
        Page,
        Show,
        Hide,
        Special,
        Finish,
        Event,
        Success,
        Fail,
        TimeOut,
        Start,
        LevelUp,
        Round,
        Clock,
        Cast,
        Ready,
        Reward,
        Hit,
        Boss,
    }

    public class TestKey
    {
        public const string USER_TOKEN = "test:userToken";
        public const string AUTH_TOKEN = "test:authToken";
        public const string TEST_URL = "test:testUrl";
        public const string MAP_INFO = "test:mapInfo";
    }

    public class UIKey
    {
        public const string DATA_KEY = "dataKey";
        public const string EVENT_KEY = "eventKey";
        public const string REFRESH_KEY = "refreshKey";
        public const string LOCAL_KEY = "localKey";
        public const string ICON_KEY = "iconKey";
        public const string ATTR_KEY = "attrKey";
    }

    public class LogInfoType
    {
        public const string UI = "UI";
        public const string DATA = "DATA";
    }

    [Serializable]
    public class SelectKey
    {
        public string type;
        public string key;
        private string[] keys = { };
        private bool isEmpty;
        private bool init;

        private void Init()
        {
            init = true;
            if (StringTools.IsEmpty(key)) isEmpty = true;
            if (type == "-1" || key == "-1") isEmpty = true;
            if (isEmpty) return;
            keys = key.Split('.');
        }

        public JSONNode Get(JSONObject obj)
        {
            if (!init) Init();
            return StringTools.GetNode(obj, keys);
        }

        public bool IsEmpty()
        {
            if (!init) Init();
            return isEmpty;
        }

        public override string ToString()
        {
            if (!init) Init();
            return String.Format("{0}->{1}", type, key);
        }
    }

    public struct SystemValue
    {
        private static int key = 0;
        private int value;

        public static SystemValue Gain
        {
            get
            {
                key += 1;
                return new SystemValue(key);
            }
        }

        public SystemValue(int i)
        {
            value = i;
        }

        public static SystemValue I(int i)
        {
            return new SystemValue(i);
        }
    }

    public class SystemEvent
    {
        public static SystemValue IsAuthorized = SystemValue.Gain;
        public static SystemValue Authorized = SystemValue.Gain;
        public static SystemValue GuestLogin = SystemValue.Gain;
        public static SystemValue AuthLogin = SystemValue.Gain;
        public static SystemValue TestLogin = SystemValue.Gain;
        public static SystemValue CodeLogin = SystemValue.Gain;
        public static SystemValue Logout = SystemValue.Gain;
        public static SystemValue Permission = SystemValue.Gain;
        public static SystemValue SyncInfo = SystemValue.Gain;

        public static SystemValue ShareWechat = SystemValue.Gain;
        public static SystemValue JumpScene = SystemValue.Gain;
        public static SystemValue BackScene = SystemValue.Gain;
        public static SystemValue ReplaceScene = SystemValue.Gain;
        public static SystemValue ChangeName = SystemValue.Gain;
        public static SystemValue ChangeAvatar = SystemValue.Gain;
        public static SystemValue ChangeCountry = SystemValue.Gain;
        public static SystemValue GetPreference = SystemValue.Gain;
        public static SystemValue UpdatePreference = SystemValue.Gain;
        public static SystemValue GetRealStatus = SystemValue.Gain;
        public static SystemValue SetRealInfo = SystemValue.Gain;
        public static SystemValue GetCount = SystemValue.Gain;
        public static SystemValue GenerateName = SystemValue.Gain;
        public static SystemValue SetRead = SystemValue.Gain;
        public static SystemValue GetChannel = SystemValue.Gain;
        public static SystemValue Stat = SystemValue.Gain;
        public static SystemValue Feedback = SystemValue.Gain;
        public static SystemValue ShowAd = SystemValue.Gain;
        public static SystemValue PreAd = SystemValue.Gain;
        public static SystemValue SubmitLeaderboard = SystemValue.Gain;
        public static SystemValue AddAchievement = SystemValue.Gain;
        public static SystemValue UpdateLocation = SystemValue.Gain;
        public static SystemValue SaveRecord = SystemValue.Gain;
        public static SystemValue LoadRecord = SystemValue.Gain;
        public static SystemValue ShareLocal = SystemValue.Gain;

        public static SystemValue InviteFriend = SystemValue.Gain;
        public static SystemValue ConfirmFriend = SystemValue.Gain;
        public static SystemValue CancelFriend = SystemValue.Gain;

        public static SystemValue SceneJoinLobby = SystemValue.Gain;
        public static SystemValue SceneLeaveLobby = SystemValue.Gain;
        public static SystemValue SceneUpdateLeaderboard = SystemValue.Gain;
        public static SystemValue SceneNumberLeaderboard = SystemValue.Gain;
        public static SystemValue SceneListLeaderboard = SystemValue.Gain;
        public static SystemValue SceneHistoryLeaderboard = SystemValue.Gain;
        public static SystemValue SceneBatchUpdateLeaderboard = SystemValue.Gain;
        public static SystemValue MapCustomList = SystemValue.Gain;
        public static SystemValue MapCustomCreate = SystemValue.Gain;
        public static SystemValue MapCustomLike = SystemValue.Gain;
        public static SystemValue MapCustomCollect = SystemValue.Gain;
        public static SystemValue MapEvent = SystemValue.Gain;

        public static SystemValue RoomInitPlayer = SystemValue.Gain;
        public static SystemValue RoomCreate = SystemValue.Gain;
        public static SystemValue RoomDisband = SystemValue.Gain;
        public static SystemValue RoomStart = SystemValue.Gain;
        public static SystemValue RoomJoinPlayer = SystemValue.Gain;
        public static SystemValue RoomLeavePlayer = SystemValue.Gain;
        public static SystemValue RoomKickPlayer = SystemValue.Gain;
        public static SystemValue RoomReadyPlayer = SystemValue.Gain;
        public static SystemValue RoomCancelPlayer = SystemValue.Gain;
        public static SystemValue RoomChangePlayer = SystemValue.Gain;
        public static SystemValue RoomChange = SystemValue.Gain;
        public static SystemValue RoomAll = SystemValue.Gain;
        public static SystemValue RoomBack = SystemValue.Gain;
        public static SystemValue RoomAllPlayer = SystemValue.Gain;

        public static SystemValue TeamInvite = SystemValue.Gain;
        public static SystemValue TeamCreate = SystemValue.Gain;
        public static SystemValue TeamKickPlayer = SystemValue.Gain;
        public static SystemValue TeamJoinPlayer = SystemValue.Gain;
        public static SystemValue TeamLeavePlayer = SystemValue.Gain;
        public static SystemValue TeamJoinLobby = SystemValue.Gain;
        public static SystemValue TeamLeaveLobby = SystemValue.Gain;
        public static SystemValue TeamReadyPlayer = SystemValue.Gain;
        public static SystemValue TeamRefresh = SystemValue.Gain;

        public static SystemValue DataSkill = SystemValue.Gain;
        public static SystemValue DataBuff = SystemValue.Gain;
        public static SystemValue DataObject = SystemValue.Gain;
        public static SystemValue DataAttr = SystemValue.Gain;
        public static SystemValue DataAttrGroup = SystemValue.Gain;

        public static SystemValue TouchEnter = SystemValue.Gain;
        public static SystemValue TouchLeave = SystemValue.Gain;
        
        public static SystemValue ConnectChat = SystemValue.Gain;
        public static SystemValue ConnectOpenChat = SystemValue.Gain;
        public static SystemValue LeaveChat = SystemValue.Gain;
        public static SystemValue OpenChat = SystemValue.Gain;
        public static SystemValue CloseChat = SystemValue.Gain;
        public static SystemValue SendChat = SystemValue.Gain;
        public static SystemValue ResetChat = SystemValue.Gain;
        public static SystemValue GetUpdateFriendChat = SystemValue.Gain;
        public static SystemValue GetListChat = SystemValue.Gain;

        public static SystemValue ValidCode = SystemValue.Gain;
        public static SystemValue Online = SystemValue.Gain;
        
        public static SystemValue TouchThreshold = SystemValue.Gain;

        public static SystemValue JumpOut = SystemValue.Gain;
    }

    public class SystemMessage
    {
        public static SystemValue None = SystemValue.Gain;
        public static SystemValue IsAuthorized = SystemValue.Gain;
        public static SystemValue Authorized = SystemValue.Gain;
        public static SystemValue AppNeedUpgrade = SystemValue.Gain;
        public static SystemValue RequestPermission = SystemValue.Gain;
        public static SystemValue RefusePermission = SystemValue.Gain;
        public static SystemValue InitApp = SystemValue.Gain;
        public static SystemValue InitAppKey = SystemValue.Gain;
        public static SystemValue InitResource = SystemValue.Gain;
        public static SystemValue ConnectSuccess = SystemValue.Gain;
        public static SystemValue ConnectFail = SystemValue.Gain;
        public static SystemValue NetworkOffline = SystemValue.Gain;
        public static SystemValue ServiceStatus = SystemValue.Gain;
        public static SystemValue Reconnecting = SystemValue.Gain;
        public static SystemValue ReconnectSuccess = SystemValue.Gain;
        public static SystemValue ReconnectFail = SystemValue.Gain;
        public static SystemValue AutoLogin = SystemValue.Gain;
        public static SystemValue AppNeedLogin = SystemValue.Gain;
        public static SystemValue LoginSuccess = SystemValue.Gain;
        public static SystemValue LogoutSuccess = SystemValue.Gain;
        public static SystemValue ChangeNameSuccess = SystemValue.Gain;
        public static SystemValue ChangeAvatarSuccess = SystemValue.Gain;
        public static SystemValue ChangeCountrySuccess = SystemValue.Gain;
        public static SystemValue SyncInfoSuccess = SystemValue.Gain;
        public static SystemValue GetPreference = SystemValue.Gain;
        public static SystemValue SetRealStatus = SystemValue.Gain;
        public static SystemValue GetRealStatus = SystemValue.Gain;
        public static SystemValue GetCount = SystemValue.Gain;
        public static SystemValue GenerateName = SystemValue.Gain;
        public static SystemValue GetChannel = SystemValue.Gain;
        public static SystemValue LoginFail = SystemValue.Gain;
        public static SystemValue GalaxyError = SystemValue.Gain;
        public static SystemValue GalaxyTimeout = SystemValue.Gain;
        public static SystemValue ShowAdSuccess = SystemValue.Gain;
        public static SystemValue LoadingProject = SystemValue.Gain;
        public static SystemValue LoadingScene = SystemValue.Gain;
        public static SystemValue LoadedScene = SystemValue.Gain;
        public static SystemValue SocialLogout = SystemValue.Gain;

        public static SystemValue NamespaceConnect = SystemValue.Gain;
        public static SystemValue NamespaceConnectSuccess = SystemValue.Gain;

        public static SystemValue AuthOtherLogin = SystemValue.Gain;
        public static SystemValue AuthInvitedFriend = SystemValue.Gain;
        public static SystemValue AuthConfirmedFriend = SystemValue.Gain;
        public static SystemValue AuthRefuseFriend = SystemValue.Gain;
        public static SystemValue EventNotice = SystemValue.Gain;

        public static SystemValue SceneInvitedTeam = SystemValue.Gain;
        public static SystemValue SceneJoinRoom = SystemValue.Gain;
        public static SystemValue SceneJoinLobby = SystemValue.Gain;
        public static SystemValue SceneLeaveLobby = SystemValue.Gain;
        public static SystemValue SceneUpdateLeaderboard = SystemValue.Gain;
        public static SystemValue SceneNumberLeaderboard = SystemValue.Gain;
        public static SystemValue SceneListLeaderboard = SystemValue.Gain;
        public static SystemValue SceneHistoryLeaderboard = SystemValue.Gain;
        public static SystemValue SceneBatchUpdateLeaderboard = SystemValue.Gain;
        public static SystemValue MapCustomList = SystemValue.Gain;
        public static SystemValue MapCustomCreate = SystemValue.Gain;
        public static SystemValue MapCustomLike = SystemValue.Gain;
        public static SystemValue MapCustomCollect = SystemValue.Gain;
        public static SystemValue MapEvent = SystemValue.Gain;

        public static SystemValue RefreshUserData = SystemValue.Gain;
        public static SystemValue LoadRecord = SystemValue.Gain;

        public static SystemValue RoomConnectSuccess = SystemValue.Gain;
        public static SystemValue RoomConnectFail = SystemValue.Gain;
        public static SystemValue RoomJoinPlayer = SystemValue.Gain;
        public static SystemValue RoomLeavePlayer = SystemValue.Gain;
        public static SystemValue RoomReadyPlayer = SystemValue.Gain;
        public static SystemValue RoomCancelPlayer = SystemValue.Gain;
        public static SystemValue RoomChangeInfo = SystemValue.Gain;
        public static SystemValue RoomChangeInfoPlayer = SystemValue.Gain;
        public static SystemValue RoomKickPlayer = SystemValue.Gain;
        public static SystemValue RoomFind = SystemValue.Gain;
        public static SystemValue RoomList = SystemValue.Gain;
        public static SystemValue RoomPlayerList = SystemValue.Gain;
        public static SystemValue RoomStart = SystemValue.Gain;
        public static SystemValue RoomCreate = SystemValue.Gain;
        public static SystemValue RoomJoin = SystemValue.Gain;

        public static SystemValue TeamConnectSuccess = SystemValue.Gain;
        public static SystemValue TeamConnectFail = SystemValue.Gain;
        public static SystemValue TeamKickPlayer = SystemValue.Gain;
        public static SystemValue TeamJoinPlayer = SystemValue.Gain;
        public static SystemValue TeamLeavePlayer = SystemValue.Gain;
        public static SystemValue TeamJoinLobby = SystemValue.Gain;
        public static SystemValue TeamLeaveLobby = SystemValue.Gain;
        public static SystemValue TeamReadyPlayer = SystemValue.Gain;
        public static SystemValue TeamChangeInfo = SystemValue.Gain;
        public static SystemValue TeamChangeInfoPlayer = SystemValue.Gain;
        public static SystemValue TeamLeave = SystemValue.Gain;
        public static SystemValue TeamCreate = SystemValue.Gain;
        public static SystemValue TeamInvited = SystemValue.Gain;

        public static SystemValue GetDataSkill = SystemValue.Gain;
        public static SystemValue GetDataBuff = SystemValue.Gain;
        public static SystemValue GetDataObject = SystemValue.Gain;
        public static SystemValue GetDataAttr = SystemValue.Gain;
        public static SystemValue GetDataAttrGroup = SystemValue.Gain;

        public static SystemValue ValidCode = SystemValue.Gain;

        public static SystemValue Init = SystemValue.Gain;
        public static SystemValue Start = SystemValue.Gain;
        public static SystemValue End = SystemValue.Gain;
        public static SystemValue Ready = SystemValue.Gain;
        public static SystemValue StartTime = SystemValue.Gain;
        public static SystemValue EndTime = SystemValue.Gain;
        public static SystemValue ShowTask = SystemValue.Gain;
        public static SystemValue UpdateTask = SystemValue.Gain;
        public static SystemValue ServiceMessage = SystemValue.Gain;
        public static SystemValue NeedRefresh = SystemValue.Gain;
        public static SystemValue Process = SystemValue.Gain;
        public static SystemValue Message = SystemValue.Gain;
        public static SystemValue Error = SystemValue.Gain;
        public static SystemValue AuthError = SystemValue.Gain;
        
        public static SystemValue ReconnectChat = SystemValue.Gain;
        public static SystemValue ConnectChat = SystemValue.Gain;
        public static SystemValue ConnectChatSuccess = SystemValue.Gain;
        public static SystemValue UnReadNumberChat = SystemValue.Gain;
        public static SystemValue UpdateFriendChat = SystemValue.Gain;
        public static SystemValue ReceiveChat = SystemValue.Gain;
        public static SystemValue ListChat = SystemValue.Gain;
    }

    [Serializable]
    public class EnvironmentSetting
    {
        public string environment;
        public string gameLink;
        public string centerLink;
        public string protocol;
    }

    [Serializable]
    public class UserSetting
    {
        public string userName;
        public string token;
        public string userId;
    }

    public interface FocusSystem
    {
        void OnFocus();
    }
    public interface PauseSystem
    {
        void OnPause();
        void OnResume();
    }
    public interface DeepLinkSystem
    {
        void OnDeepLink(Uri url);
    }
    public interface QuitSystem
    {
        void OnQuit();
    }
    public class Game : MonoBehaviour, ClientIpCustom
    {
        public static GameObject GO;
        public static Game single;

        private bool audio;
        private AudioClip bgm;
        private AudioMixer audioMixer;
        private AudioSource audioBGMSource;
        private AudioSource audioEFFSource;

        internal List<GameManager> _managerList = new List<GameManager>();
        internal List<LoopTimer> _sceneTimers = new List<LoopTimer>();

        public static OfflineDataSettings OfflineDataSettings;
        
        internal const string DefaultSettingsAssetFile = "OfflineDataSettings";
        public static bool DefaultCustomIpSettings(string file = "")
        {
            if (file == "")
            {
                file =  DefaultSettingsAssetFile;
            }
            var settings = (OfflineDataSettings)Resources.Load(file, typeof(OfflineDataSettings));
            if (settings == null)
            {
                return false;
            }
            OfflineDataSettings = settings;
            if (settings.Url.IsNullOrEmpty() || settings.Key.IsNullOrEmpty())
            {
                Debug.LogError("default custom ip miss");
                return false;
            }
            return true;
        }

        public delegate void OnApplicationDelegate();

        public delegate void OnDeepLinkDelegate(Uri url);

        public event OnApplicationDelegate OnPauseEvent;
        public event OnApplicationDelegate OnResumeEvent;
        public event OnApplicationDelegate OnFocusEvent;
        public event OnDeepLinkDelegate OnDeepLinkEvent;
        public event OnApplicationDelegate OnQuitEvent;

        public static int TeamMaxNum = 0;
        public int initApp = 0;

        public static void Init()
        {
            GO = new GameObject("Game");
            DontDestroyOnLoad(GO);
            
            SystemInput.Init();
            CacheTools.LoadData();

            single = GO.AddComponent<Game>();
            single.InitAudio();
            single.InitScreen();

            var world = World.DefaultGameObjectInjectionWorld;
            world.GetOrCreateSystemManaged<PhysicsSystemGroup>().Enabled = false;
            // world.GetOrCreateSystem<StepPhysicsWorld>().Enabled(false);
            // world.GetOrCreateSystemManaged<ExportPhysicsWorld>().Enabled = false;
            // world.GetOrCreateSystem<EndFramePhysicsSystem>().Enabled(false);
            // world.GetOrCreateSystem<RenderMeshSystemV2>().Enabled(false);

            LogTools.Process("Game start");
        }
        void Awake()
        {
            
            Application.deepLinkActivated += OnDeepLinkActivated;
            if (!string.IsNullOrEmpty(Application.absoluteURL))
            {
                // Cold start and Application.absoluteURL not null so process Deep Link.
                OnDeepLinkActivated(Application.absoluteURL);
            }
        }

        public void RegisterManager(GameManager manager)
        {
            _managerList.Add(manager);
            manager.OnInit();
            if (Project.currentPage != null) manager.OnPage(Project.currentPage);
        }

        public void RegisterPause(PauseSystem system)
        {
            OnPauseEvent += system.OnPause;
            OnResumeEvent += system.OnResume;
        }
        public void UnRegisterPause(PauseSystem system)
        {
            OnPauseEvent -= system.OnPause;
            OnResumeEvent -= system.OnResume;
        }

        public void RegisterFocus(FocusSystem system)
        {
            OnFocusEvent += system.OnFocus;
        }

        public void UnRegisterFocus(FocusSystem system)
        {
            OnFocusEvent -= system.OnFocus;
        }
        public void RegisterDeepLink(DeepLinkSystem system)
        {
            OnDeepLinkEvent += system.OnDeepLink;
        }

        public void RegisterQuit(QuitSystem system)
        {
            OnQuitEvent += system.OnQuit;
        }
        private async void Update()
        {
            for (int i = 0; i < _managerList.Count; i++)
            {
                await _managerList[i].OnUpdate();
            }
        }

        private void OnDestroy()
        {
            for (int i = 0; i < _managerList.Count; i++)
            {
                _managerList[i].OnDestroy();
            }
        }

        public static string GetVersion()
        {
            return Application.version;
        }

        public static string GetPlatform()
        {
            return Application.platform.ToString();
        }

        public static string GetDevice()
        {
            return SystemInfo.deviceModel;
        }

        public static string GetDevicePlatform()
        {
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX || UNITY_EDITOR
            return "PC";
#elif UNITY_ANDROID || UNITY_IOS
            return "Mobile";
#endif
            return "Other";
        }

        public static bool IsPc()
        {
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX || UNITY_EDITOR
            return true;
#elif UNITY_ANDROID || UNITY_IOS
            return false;
#endif
            return false;
        }

        public static bool IsMobile()
        {
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX || UNITY_EDITOR
            return false;
#elif UNITY_ANDROID || UNITY_IOS
            return true;
#endif
            return false;
        }

        public void ChangeSpeed(float value)
        {
            Time.timeScale = 1.0f * value;
        }

        public void RecoverySpeed()
        {
            Time.timeScale = 1.0f;
        }

        public void UpdateScale(float i)
        {
            float t = (float)i / 10f;
            ScalableBufferManager.ResizeBuffers(t, t);
        }

        public int GetQuality()
        {
            return CacheTools.GetVal(CacheKey.QUALITY, QualitySettings.GetQualityLevel());
        }

        public void UpdateQuality(int i)
        {
            LogTools.Info("UpdateQuality: {0}", i);
            QualitySettings.SetQualityLevel(i);
            CacheTools.SetVal(CacheKey.QUALITY, i);
        }

        public int GetFull()
        {
            return CacheTools.GetVal(CacheKey.FULL, Screen.fullScreen ? 1 : 0);
        }

        public void UpdateFull(int i)
        {
            SystemInput.full = i > 0;
            LogTools.Info("UpdateFull: {0} {1}", i, SystemInput.full);
            Screen.fullScreen = SystemInput.full;
            CacheTools.SetVal(CacheKey.FULL, i);
            SystemInput.resolution = new Vector2(Screen.currentResolution.width, Screen.currentResolution.height);
            if (SystemInput.full)
            {
                UpdateResolution(GetResolution());
            }
            else
            {
                UpdateSize(GetSize());
            }
        }

        public int GetResolution()
        {
#if UNITY_ANDROID || UNITY_IOS
            return CacheTools.GetVal(CacheKey.RESOLUTION, 75);
#elif UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX
            return CacheTools.GetVal(CacheKey.RESOLUTION, 100);
#endif
            return CacheTools.GetVal(CacheKey.RESOLUTION, 75);
        }

        public string GetSize()
        {
            return CacheTools.GetVal(CacheKey.SIZE, ScreenTools.GetName(ScreenTools.Type.Size, 0));
        }

        public void UpdateSize(string value)
        {
            LogTools.Info("UpdateSize: {0}", value);
            string[] values = value.Split("x");
            int x = Convert.ToInt32(values[0]);
            int y = Convert.ToInt32(values[1]);
            Screen.SetResolution(x, y, SystemInput.full);
            CacheTools.SetVal(CacheKey.SIZE, value);
        }

        public void UpdateResolution(int i)
        {
            LogTools.Info("UpdateResolution: {0}", i);
            float value = (float)i / 100f;
            Screen.SetResolution((int)(SystemInput.resolution.x * value), (int)(SystemInput.resolution.y * value), SystemInput.full);
            CacheTools.SetVal(CacheKey.RESOLUTION, i);
        }

        public int GetFrameRate()
        {
            return CacheTools.GetVal(CacheKey.FRAME_RATE, 30);
        }

        public void UpdateFrameRate(int i)
        {
            LogTools.Info("UpdateFrameRate: {0}", i);
            Application.targetFrameRate = i;
            CacheTools.SetVal(CacheKey.FRAME_RATE, i);
        }

        private void InitScreen()
        {
            // Screen.SetResolution((int)(Screen.currentResolution.width * 0.5), (int)(Screen.currentResolution.height * 0.5), Screen.fullScreen, 30);
            UpdateQuality(GetQuality());
            UpdateFrameRate(GetFrameRate());
            UpdateFull(GetFull());
        }

        public string GetLanguage()
        {
            // TODO 暂时多语言不支持，默认中文
            return Localization.LanguageToString(Language.Chinese);
            // return CacheTools.GetVal(CacheKey.LANGUAGE, Localization.GetLanguageString());
            return CacheTools.GetVal(CacheKey.LANGUAGE, Localization.LanguageToString(Language.Chinese));
        }

        public void ChangeLanguage(string language)
        {
            CacheTools.SetVal(CacheKey.LANGUAGE, language);
            CacheTools.RemovePrefix(CachePrefix.API);
            Localization.Instance.Refresh(language);
            Project.ChangeLocation();
        }

        public void InitAudio()
        {
            audioMixer = (AudioMixer)Resources.Load("AudioMixer", typeof(AudioMixer));
            audioBGMSource = gameObject.AddComponent<AudioSource>();
            audioBGMSource.loop = true;
            audioBGMSource.outputAudioMixerGroup = audioMixer.FindMatchingGroups(CacheKey.BGM)[0];
            audioEFFSource = gameObject.AddComponent<AudioSource>();
            audioEFFSource.outputAudioMixerGroup = audioMixer.FindMatchingGroups(CacheKey.EFF)[0];
            WaitSeconds(1, () =>
            {
                audioMixer.SetFloat(CacheKey.ALL, MathTools.FormatAudioFader(GetALLVolume()));
                audioMixer.SetFloat(CacheKey.BGM, MathTools.FormatAudioFader(GetBGMVolume()));
                audioMixer.SetFloat(CacheKey.EFF, MathTools.FormatAudioFader(GetEFFVolume()));
                audio = true;
                if (bgm != null) PlayBGM(bgm);
            });
        }

        public int GetALLVolume()
        {
            return CacheTools.GetVal(CacheKey.ALL, 100);
        }

        public void SetALLVolume(int v)
        {
            CacheTools.SetVal(CacheKey.ALL, v);
            audioMixer.SetFloat(CacheKey.ALL, MathTools.FormatAudioFader(v));
        }

        public int GetBGMVolume()
        {
            return CacheTools.GetVal(CacheKey.BGM, 50);
        }

        public void SetBGMVolume(int v)
        {
            CacheTools.SetVal(CacheKey.BGM, v);
            audioMixer.SetFloat(CacheKey.BGM, MathTools.FormatAudioFader(v));
        }

        public int GetEFFVolume()
        {
            return CacheTools.GetVal(CacheKey.EFF, 50);
        }

        public void SetEFFVolume(int v)
        {
            CacheTools.SetVal(CacheKey.EFF, v);
            audioMixer.SetFloat(CacheKey.EFF, MathTools.FormatAudioFader(v));
        }

        public void PlayBGM(AudioClip clip)
        {
            if (!audio)
            {
                bgm = clip;
                return;
            }
            if (clip != null)
            {
                if (audioBGMSource.clip != clip || !audioBGMSource.isPlaying)
                {
                    audioBGMSource.clip = clip;
                    audioBGMSource.Play();
                }
            }
        }

        public void StopBGM()
        {
            audioBGMSource.Stop();
        }

        public void StopEFF()
        {
            audioEFFSource.Stop();
        }

        public void PlayEFF(SoundType type)
        {
            if (!audio)
            {
                return;
            }
            
            var clip = SoundTools.Get(type);
            // Debug.LogErrorFormat("PlayEFF: type {0}", type);
            if (clip != null)
            {
                audioEFFSource.PlayOneShot(clip);
            }
        }

        public void PlayEFF(AudioClip clip)
        {
            // Debug.LogErrorFormat("PlayEFF: clip {0}", clip);
            if (clip != null)
            {
                audioEFFSource.PlayOneShot(clip);
            }
        }

        public void PlayEFF(AudioClip clip, Vector3 position)
        {
            if (clip != null)
            {
                AudioSource.PlayClipAtPoint(clip, position, audioEFFSource.volume);
            }
        }

        public static void NeedSaveImage()
        {
#if UNITY_ANDROID && !UNITY_EDITOR
            // 检查 Android 版本
            if (GetAndroidVersion() >= 30) // Android 11 (API 30)
            {
                // 请求 MANAGE_EXTERNAL_STORAGE 权限
                if (!Permission.HasUserAuthorizedPermission("android.permission.MANAGE_EXTERNAL_STORAGE"))
                {
                    Permission.RequestUserPermission("android.permission.MANAGE_EXTERNAL_STORAGE");
                }
            }
            else
            {
                // 请求 WRITE_EXTERNAL_STORAGE 权限
                if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
                {
                    Permission.RequestUserPermission(Permission.ExternalStorageWrite);
                }
            }
#endif
        }

        public void CaptureScreenshotTmp(Action<bool, string> callback)
        {
            CaptureScreenshot("ScreenShot", "tmp.png", callback);
        }
        
        public void CaptureScreenshot(string dir, string fileName, Action<bool, string> callback)
        {
            string path = Application.persistentDataPath + "/" + dir;
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
            path = path + "/" + fileName;
            if (File.Exists(path)) File.Delete(path);
            try
            {
#if UNITY_EDITOR
                ScreenCapture.CaptureScreenshot(path);
#elif UNITY_ANDROID
                ScreenCapture.CaptureScreenshot(dir + "/" + fileName);
#endif
                // 等待一帧，确保文件写入完成
                WaitSeconds(1, () =>
                {
                    // 检查文件是否存在
                    if (File.Exists(path))
                    {
                        callback(true, path); // 截图成功
                    }
                    else
                    {
                        callback(false, path);
                    }
                });
            }
            catch (System.Exception ex)
            {
                Debug.LogError("截图失败，错误信息: " + ex.Message);
                callback(false, "");
            }
        }
        
        /// <summary>
        /// 获取 Android 设备的系统版本（API 级别）
        /// </summary>
        /// <returns>Android 版本对应的 API 级别（例如 Android 11 返回 30）</returns>
        public static int GetAndroidVersion()
        {
            int apiLevel = 0;

            // 使用 AndroidJavaClass 访问 Android 的 Build.VERSION 类
            using (AndroidJavaClass versionClass = new AndroidJavaClass("android.os.Build$VERSION"))
            {
                // 获取 SDK_INT 属性
                apiLevel = versionClass.GetStatic<int>("SDK_INT");
            }

            return apiLevel;
        }

        public bool CheckNetwork()
        {
            switch (Application.internetReachability)
            {
                case NetworkReachability.NotReachable:
                    //without network
                    return false;
            }

            return true;
        }

        public LoopTimer GlobalLoop(float time, Action CB)
        {
            return new LoopTimer(this, time, CB);
        }

        public LoopTimer SceneLoop(float time, Action CB)
        {
            var timer = GlobalLoop(time, CB);
            _sceneTimers.Add(timer);
            return timer;
        }

        public void ClearLoop()
        {
            // 清理场景定时器
            var e = _sceneTimers.GetEnumerator();
            while (e.MoveNext())
            {
                if (e.Current != null)
                    e.Current.Stop();
            }
            e.Dispose();
            _sceneTimers.Clear();
        }

        public void WaitSeconds(float time, Action CB)
        {
            StartCoroutine(WaitSecondsAsync(time, null, CB));
        }
        
        public void LoopSeconds(float time, Func<bool> judge, Action CB)
        {
            StartCoroutine(WaitSecondsAsync(time, judge, CB));
        }

        internal static IEnumerator WaitSecondsAsync(float time, Func<bool> judge, Action CB)
        {
            if (judge == null)
            {
                yield return new WaitForSeconds(time);
                CB();
            }
            else
            {
                do
                {
                    yield return new WaitForSeconds(time);
                    if (judge()) CB();
                } while (judge());
            }
        }

        public void LoadImage(string url, Action<bool, Texture2D, string> CB)
        {
            if (url == null || url == "")
                return;
            StartCoroutine(LoadUrlAsyn(url, "imageTmp", "png", (success, www, message) =>
            {
                if (success)
                {
                    CB(true, www.texture, "success");
                }
                else
                {
                    CB(false, null, message);
                }
            }));
        }

        public void LoadAssetbundle(string url, Action<bool, AssetBundle, string> CB)
        {
            if (url == null || url == "")
                return;
            StartCoroutine(LoadUrlAsyn(url, "assetBundleTmp", "model", (success, www, message) =>
            {
                if (success)
                {
                    CB(true, www.assetBundle, "success");
                }
                else
                {
                    CB(false, null, message);
                }
            }));
        }

        public void StartCoroutine(System.Threading.Tasks.Task task)
        {
            StartCoroutine(Task(task));
        }

        private IEnumerator Task(System.Threading.Tasks.Task task)
        {
            yield return task;
        }
        
        void OnApplicationFocus(bool hasFocus)
        {
            // Debug.LogError("focus:"+hasFocus);
            if (initApp < 2 && hasFocus)
            {
                initApp += 1;
                return;
            }
            if (hasFocus)
            {
                if (OnFocusEvent != null) OnFocusEvent();
            }
        }

        void OnApplicationPause(bool pauseStatus)
        {
            // Debug.LogError("pause:"+pauseStatus);
            if (initApp < 2 && !pauseStatus)
            {
                initApp += 1;
                return;
            }
            if (pauseStatus)
            {
                if (OnPauseEvent != null) OnPauseEvent();
            }
            else
            {
                if (OnResumeEvent != null) OnResumeEvent();
            }
        }

        private void OnApplicationQuit()
        {
            if (OnQuitEvent != null) OnQuitEvent();
        }

        private void OnDeepLinkActivated(string url)
        {
            var deeplinkURL = url;
            Uri uri = new Uri(url);
            if (OnDeepLinkEvent != null) OnDeepLinkEvent(uri);
        }


        IEnumerator LoadUrlAsyn(string url, string folderName, string suffix, Action<bool, WWW, string> CB)
        {
            string path = Application.persistentDataPath;
            string fileName = StringTools.Md5(url) + "." + suffix;
            string localUrl = path + "/" + folderName + "/" + fileName;
            bool local = false;
            WWW www;
            if (new FileInfo(localUrl).Exists)
            {
                www = new WWW("file://" + localUrl);
                local = true;
            }
            else
            {
                www = new WWW(url);
            }
            yield return www;
            if (www != null && www.isDone && string.IsNullOrEmpty(www.error))
            {
                CB(true, www, "success");
                if (!local)
                {
                    FileTools.CreateFolder(path, folderName);
                    FileTools.SaveFile(path + "/" + folderName + "/" + fileName, www.text);
                }
            }
            else
            {
                CB(false, null, "is error!");
            }
        }

        public void UploadOSS(string filepath, JSONNode result, string mime, Action<bool, string, string> CB)
        {
            string host = result["host"];
            string key = result["key"];
            string OSSAccessKeyId = result["OSSAccessKeyId"];
            string policy = result["policy"];
            string signature = result["signature"];
            string url = result["url"];
            FileInfo file = new FileInfo(filepath);
            Dictionary<string, string> headers = new Dictionary<string, string>();
            var boundary = "--" + StringTools.GenerateStr(32) + "--";
            headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
            var requestBody = "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"key\"\r\n"
                              + "\r\n" + key + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"OSSAccessKeyId\"\r\n"
                              + "\r\n" + OSSAccessKeyId + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"policy\"\r\n"
                              + "\r\n" + policy + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"Signature\"\r\n"
                              + "\r\n" + signature + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"file\"; filename=\"" + file.Name + "\"\r\n"
                              + "Content-Type: " + mime + "\r\n"
                              + "\r\n";
            var requestBodyByte = System.Text.Encoding.UTF8.GetBytes(requestBody);
            var fileByte = File.ReadAllBytes(filepath);

            var lastBody = "\r\n--" + boundary + "--\r\n";
            var requestlastBodyByte = System.Text.Encoding.UTF8.GetBytes(lastBody);

            //WWWForm wf = new WWWForm();
            //headers.Add("Content-Length", requestBody.Length.ToString());
            //wf.AddField("OSSAccessKeyId", OSSAccessKeyId);
            //wf.AddField("policy", policy);
            //wf.AddField("Signature", signature);
            //wf.AddField("key", key);
            //wf.AddField("success_action_status", result["success_action_status"].AsInt);
            //wf.AddBinaryData("file", File.ReadAllBytes(filepath), file.Name, mime);
            var by = new byte[requestBodyByte.Length + fileByte.Length + requestlastBodyByte.Length];

            Array.Copy(requestBodyByte, 0, by, 0, requestBodyByte.Length);
            Array.Copy(fileByte, 0, by, requestBodyByte.Length, fileByte.Length);
            Array.Copy(requestlastBodyByte, 0, by, requestBodyByte.Length + fileByte.Length, requestlastBodyByte.Length);
            StartCoroutine(HttpTools.GetInstance().Post(result["host"], by, headers, (success, www) =>
            {
                if (success)
                {
                    CB(true, url.Replace("{key}", key), "success");
                }
                else
                {
                    CB(false, null, www.error);
                }

            }));
        }

        public void GetAddressList(string domain, Action<string[]> callback)
        {
            using var client = new WebClient();
            client.Headers.Add("User-Agent", "C# console program");

            var domainData = CryptoTools.Base64UrlEncodeNoPadding(CryptoTools.EncryptDES(Encoding.ASCII.GetBytes("domain://"+domain), OfflineDataSettings.Key));
            try
            {
                var dd = new Uri(OfflineDataSettings.Url+"/settings/"+domainData);
                Debug.LogError("address:" + OfflineDataSettings.Url+"/settings/"+domainData+"("+domain+")");
                client.DownloadStringCompleted += (sender, args) =>
                {
                    // Debug.LogError("address:" + domainData+"("+domain+")" + "=>" + data);
                    try
                    {
                        var data = args.Result;
                        var content = Encoding.ASCII.GetString(CryptoTools.DecryptDES(System.Convert.FromBase64String(data), OfflineDataSettings.Key));
                        if (content.IsNullOrEmpty())
                        {
                            callback(null);
                        }
                        var addresss = content.Split(",");
                        callback(addresss);
                    }catch (Exception exception)
                    {
                        Debug.LogException(exception);
                        callback(null);
                    }
                };
                client.DownloadStringAsync(dd);
            }
            catch (Exception exception)
            {
                Debug.LogException(exception);
                callback(null);
            }
        }

        public void GetAddress(string domain, Action<Uri> callback)
        {
            GetAddressList(domain, (addresss) =>
            {
                var r = new Random();
                var address = addresss[r.Next(addresss.Length)];
                var u = new Uri("http://" + address);
                Project.domainUriMap[domain] = u;
                callback(u);
            });
        }

        public void GetServiceStatus(string domain, Action<string> callback)
        {
            using var client = new WebClient();
            client.Headers.Add("User-Agent", "C# console program");
            
            var domainData = CryptoTools.Base64UrlEncodeNoPadding(CryptoTools.EncryptDES(Encoding.ASCII.GetBytes("domain://"+domain), OfflineDataSettings.Key));
            try
            {
                var dd = new Uri(OfflineDataSettings.Url+"/status/"+domainData);
                Debug.LogError("status:" + OfflineDataSettings.Url+"/status/"+domainData+"("+OfflineDataSettings.Service+")");
                client.DownloadStringCompleted += (sender, args) =>
                {
                    // Debug.LogError("status:" + domainData+"("+OfflineDataSettings.Service+")" + "=>" + args.Result);
                    try
                    {
                        var data = args.Result;
                        var content = Encoding.ASCII.GetString(CryptoTools.DecryptDES(System.Convert.FromBase64String(data), OfflineDataSettings.Key));
                        if (content.IsNullOrEmpty())
                        {
                            callback(null);
                        }
                        callback(content);
                    }catch (Exception exception)
                    {
                        Debug.LogException(exception);
                        callback(null);
                    }
                };
                client.DownloadStringAsync(dd);
            }
            catch (Exception exception)
            {
                Debug.LogException(exception);
                callback(null);
            }
        }
        public Uri GetAddress(string domain)
        {
            if (Project.domainUriMap.TryGetValue(domain, out var url))
            {
                return url;
            }

            GetAddress(domain, (url) =>
            {

            });
            return null;
        }
        public void GetHotmap(string mapNoId, string type, Action<byte[][]> callback)
        {
            using var client = new WebClient();
            client.Headers.Add("User-Agent", "C# console program");
            
            var datakey = CryptoTools.Base64UrlEncodeNoPadding(CryptoTools.EncryptDES(Encoding.ASCII.GetBytes(mapNoId+"/"+type+"/"), OfflineDataSettings.Key));
            try
            {
                var dd = new Uri(OfflineDataSettings.Url+"/hotmap/"+OfflineDataSettings.Env+"/"+datakey);
                Debug.LogError("hotmap:" + OfflineDataSettings.Url+"/hotmap/"+OfflineDataSettings.Env+"/"+datakey+"("+mapNoId+":"+type+")");
                client.DownloadDataCompleted += async (sender, args) =>
                {
                    // Debug.LogError("status:" + domainData+"("+OfflineDataSettings.Service+")" + "=>" + args.Result);
                    try
                    {
                        var content = args.Result;
                        using (MemoryStream writeStream = new MemoryStream(content.Length))
                        {
                            await writeStream.WriteAsync(content);
                            writeStream.Seek(0, SeekOrigin.Begin);
                            await using var decompressor = new GZipStream(writeStream, CompressionMode.Decompress);
                            using (MemoryStream readStream = new MemoryStream(100))
                            {
                                await decompressor.CopyToAsync(readStream);
                                readStream.Seek(0, SeekOrigin.Begin);
                                var info = MessagePackSerializer.Deserialize<object[]>(readStream);
                                var bb = new byte[info.Length][];
                                for (var i = 0; i < info.Length; i++)
                                {
                                    bb[i] = (byte[])info[i];
                                }
                                callback(bb);
                            }
                        }
                    }catch (Exception exception)
                    {
                        Debug.LogException(exception);
                        callback(null);
                    }
                };
                client.DownloadDataAsync(dd);
            }
            catch (Exception exception)
            {
                Debug.LogException(exception);
                callback(null);
            }
        }
        public void GetRoadmap(string mapNoId, Action<Vector2Int[][]> callback)
        {
            using var client = new WebClient();
            client.Headers.Add("User-Agent", "C# console program");
            
            var datakey = CryptoTools.Base64UrlEncodeNoPadding(CryptoTools.EncryptDES(Encoding.ASCII.GetBytes(mapNoId+"/build/"), OfflineDataSettings.Key));
            try
            {
                var dd = new Uri(OfflineDataSettings.Url+"/roadmap/"+OfflineDataSettings.Env+"/"+datakey);
                Debug.LogError("roadmap:" + OfflineDataSettings.Url+"/roadmap/"+OfflineDataSettings.Env+"/"+datakey+"("+mapNoId+")");
                client.DownloadDataCompleted += async (sender, args) =>
                {
                    // Debug.LogError("status:" + domainData+"("+OfflineDataSettings.Service+")" + "=>" + args.Result);
                    try
                    {
                        var content = args.Result;
                        using (MemoryStream writeStream = new MemoryStream(content.Length))
                        {
                            await writeStream.WriteAsync(content);
                            writeStream.Seek(0, SeekOrigin.Begin);
                            await using var decompressor = new GZipStream(writeStream, CompressionMode.Decompress);
                            using (MemoryStream readStream = new MemoryStream(100))
                            {
                                await decompressor.CopyToAsync(readStream);
                                readStream.Seek(0, SeekOrigin.Begin);
                                var info = MessagePackSerializer.Deserialize<object[]>(readStream);
                                var bb = new Vector2Int[info.Length][];
                                for (var i = 0; i < info.Length; i++)
                                {
                                    var c = (object[])info[i];
                                    bb[i] = new Vector2Int[c.Length];
                                    for (var j = 0; j < c.Length; j++)
                                    {
                                        bb[i][j] = SerializeInt.Byte2Vector((byte[])c[j]);
                                    }
                                }
                                callback(bb);
                            }
                        }
                    }catch (Exception exception)
                    {
                        Debug.LogException(exception);
                        callback(null);
                    }
                };
                client.DownloadDataAsync(dd);
            }
            catch (Exception exception)
            {
                Debug.LogException(exception);
                callback(null);
            }
        }
    }
}